home *** CD-ROM | disk | FTP | other *** search
- # include <ingres.h>
- # include <tree.h>
- # include <symbol.h>
- # include "globs.h"
- # include <sccs.h>
- # include <errors.h>
-
- SCCSID(@(#)aggregate.c 8.3 2/8/85)
-
-
-
-
- /*
- ** AGGREGATE - replace aggregates with their values
- **
- ** Aggregate attempts to optimize aggregate processing
- ** wherever possible. It replaces aggregates with their
- ** values, and links aggregate functions which have
- ** identical by-lists together.
- **
- ** Note that for the sake of this code, A "prime"
- ** aggregate is one in which duplicates are removed.
- ** These are COUNTU, SUMU, and AVGU.
- **
- ** Aggregate first forms a list of all aggregates in
- ** the order they should be processed.
- **
- ** For each aggregate, it looks at all other aggregates
- ** to see if there are two simple aggregates
- ** or if there is another aggregate funtion with the
- ** same by-list.
- **
- ** An attempt is made to run
- ** as many aggregates as possible at once. This can be
- ** done only if two or more aggregates have the same
- ** qualifications and in the case of aggregate functions,
- ** they must have identical by-lists.
- ** Even then, certain combinations
- ** of aggregates cannot occure together. The list is
- ** itemized later in the code.
- **
- ** Aggregate calls BYEVAL or AGEVAL to actually process
- ** aggregate functions or aggregates respectively.
- **
- ** Trace Flags:
- ** 40
- */
-
- aggregate(root)
- QTREE *root;
- {
- struct agglist alist[MAXAGG + 1];
- QTREE *rlist[MAXAGG + 1];
- struct agglist *al, *al1;
- register QTREE *agg, *aop1, *r;
- QTREE *aop, *agg1;
- int i, simple_agg, varmap;
- int attcnt, anyagg, attoff, twidth;
- QTREE *makavar(), *agspace();
- extern char *rangename();
- extern QTREE *ageval();
- extern QTREE *byeval();
-
- al = alist;
- De.de_aggnext = al;
- De.de_agglim = &al[MAXAGG];
-
- findagg(&root, root); /* generate list of all aggregates */
- De.de_aggnext->agpoint = 0; /* mark end of the list */
- anyagg = 0;
-
- varmap = root->sym.value.sym_root.lvarm | root->sym.value.sym_root.rvarm;
-
- /* process each aggregate */
- for (;agg = al->agpoint; al++)
- {
- /* is this still an aggregate? */
- if (agg->sym.type != AGHEAD)
- continue;
- mapvar(agg, 0); /* map the aggregate tree */
- anyagg++;
-
- De.de_sourcevar = bitpos(agg->sym.value.sym_root.lvarm | agg->sym.value.sym_root.rvarm);
- # ifdef xDTR1
- if (tTf(40, 4))
- printf("De.de_sourcevar=%d,rel=%s\n", De.de_sourcevar, rangename(De.de_sourcevar));
- # endif
-
- simple_agg = (agg->left->sym.type == AOP); /* TRUE if no bylist */
- aop = agg->left; /* assume it was TRUE */
- # ifdef xDTR1
- if (tTf(40, 0))
- printf("%s\n", simple_agg ? "agg" : "agg-func");
- # endif
- if (simple_agg)
- {
- /* simple aggregate */
- rlist[0] = agspace(aop);
- twidth = aop->sym.value.sym_op.opfrml & I1MASK; /* init width to the width of the aggregate */
- }
- else
- {
- attoff = agg->left->left->sym.value.sym_resdom.resno + 2;
- aop = aop->right; /* find the AOP node */
- /* assign new source variable for aggregate */
- al->agvarno = getrange(&varmap);
- /* compute width of bylist + count field */
- twidth = findwid(agg->left) + 4;
-
- /* make sure the aggregate does not exceed max dimensions */
- if (chkwidth(aop, &twidth, attoff))
- derror(AGFTOBIG);
- }
- attcnt = 1; /* one aggregate so far */
-
- /* look for another identical aggregate */
- for (al1 = al + 1; agg1 = al1->agpoint; al1++)
- {
-
- /* if agg is nested inside agg1 then ignore it */
- if (al->agfather == agg1 || agg1->sym.type != AGHEAD)
- {
- continue;
- }
-
- /* split aggs and agg-func apart */
- /* check for identical aggregate */
- if (simple_agg)
- {
- aop1 = agg1->left; /* find AOP node */
-
- if (aop1->sym.type != AOP)
- continue; /* not a simple agg */
-
- /* make sure they can be run together */
- if (checkagg(agg, aop, agg1, aop1) == 0)
- continue;
-
- if ((i = sameagg(agg, aop1, attcnt)) >= 0)
- {
- /* found identical aggregate to rlist[i] */
- r = rlist[i];
- }
- else
- {
- /* put this agg in with the others */
-
- /* first make sure it won't exceed tuple length */
- if (chkwidth(aop1, &twidth, 0))
- continue; /* can't be included */
- r = rlist[attcnt++] = agspace(aop1);
-
- /* connect into tree */
- aop1->left = agg->left;
- agg->left = aop1;
- }
- }
- else
- {
- /* aggregate function */
- if (!sameafcn(agg->left->left, agg1->left->left))
- continue;
-
- aop1 = agg1->left->right; /* find AOP */
-
-
- if (checkagg(agg, aop, agg1, aop1) == 0)
- {
- /* same by-lists but they can't be run together */
- continue;
- }
-
- if ((i = sameagg(agg, aop1, attcnt)) < 0)
- {
- /* make sure there is room */
- if (chkwidth(aop1, &twidth, attcnt + attoff))
- continue;
-
- /* add aggregate into tree */
- i = attcnt++;
-
- aop1->left = agg->left->right;
- agg->left->right = aop1;
- }
-
- r = makavar(aop1, al->agvarno, i + attoff);
- }
- /* replace aggregate in original tree with its value */
- *(al1->father) = r;
-
- /* remove aggregate from local list */
- agg1->sym.type = -1;
- # ifdef xDTR1
- if (tTf(40, 3))
- printf("including aghead %x\n", agg1);
- # endif
- }
-
- /* process aggregate */
- if (simple_agg)
- {
- rlist[attcnt] = 0;
- ageval(agg, rlist); /* pass tree and result list */
- r = rlist[0];
- }
- else
- {
- opt_bylist(alist, agg);
- byeval(al->agfather, agg, al->agvarno);
- r = makavar(aop, al->agvarno, attoff);
- }
- /*
- ** Link result into tree. al->father hold the address
- ** &(tree->left) or &(tree->right).
- */
- *(al->father) = r;
- # ifdef xDTR1
- if (tTf(40, 4))
- {
- printf("agg value\n");
- treepr(*(al->father));
- }
- # endif
- }
- if (anyagg)
- {
- opt_bylist(alist, root);
- mapvar(root, 0); /* remap main tree */
- }
- }
- /*
- ** findagg builds a list of all aggregates
- ** in the tree. It finds them by leftmost
- ** innermost first.
- **
- ** The parameters represent:
- ** nodep: the address of the node pointing to you
- ** eg. &(tree->left) or &(tree->right)
- ** agf: the root node. or if we are inside
- ** a nested aggregate, the AGHEAD node
- */
-
- findagg(nodep, agf)
- QTREE **nodep;
- QTREE *agf;
- {
- register QTREE *q, *f;
- register struct agglist *list;
- int agg;
-
- if ((q = *nodep) == NULL)
- return;
-
- f = agf;
- if (agg = (q->sym.type == AGHEAD))
- f = q; /* this aggregate is now the father root */
-
- /* find all aggregates below */
- findagg(&(q->left), f);
- findagg(&(q->right), f);
-
- /* if this is an aggregate, put it on the list */
- if (agg)
- {
- if (De.de_aggnext >= De.de_agglim)
- derror(TOOMANYAGGS);
- list = De.de_aggnext;
- list->father = nodep;
- list->agpoint = q;
- list->agfather = agf;
- De.de_aggnext++;
- }
- }
- /*
- ** Agspace allocates space for the result of
- ** a simple aggregate.
- */
-
- QTREE *
- agspace(aop)
- QTREE *aop;
- {
- register QTREE *a, *r;
- register int length;
- extern char *need();
-
- a = aop;
- length = a->sym.value.sym_op.opfrml & I1MASK;
- r = (QTREE *) need(De.de_qbuf, length + QT_HDR_SIZ);
- r->left = r->right = 0;
- r->sym.type = a->sym.value.sym_op.opfrmt;
- r->sym.len = length;
-
- return (r);
- }
- /*
- ** Chkwidth -- make sure that the inclusion of another aggregate will
- ** not exceed the system limit. This means that the total width
- ** cannot exceed MAXTUP and the number of domains cannot exceed MAXDOM-1
- */
-
- chkwidth(aop, widthp, domno)
- QTREE *aop;
- int *widthp;
- int domno;
- {
- register int width;
-
- width = *widthp;
-
- # ifdef xDTR1
- if (tTf(40, 10))
- printf("agg width %d,dom %d\n", width, domno);
- # endif
-
- width += (aop->sym.value.sym_op.opfrml & I1MASK);
-
- if (width > MAXTUP || domno > MAXDOM - 1)
- return (1);
-
- *widthp = width;
- return (0);
- }
- /*
- ** Determine whether an aggregate is prime
- ** or a don't care aggregate. Returns TRUE
- ** if COUNTU,SUMU,AVGU,MIN,MAX,ANY.
- ** Returns false if COUNT,SUM,AVG.
- */
-
- cprime(aop)
- QTREE *aop;
- {
- register int i;
-
- i = TRUE;
- switch (aop->sym.value.sym_op.opno)
- {
- case opCOUNT:
- case opSUM:
- case opAVG:
- i = FALSE;
- }
- return (i);
- }
- /*
- ** Getrange find a free slot in the range table
- ** for an aggregate function.
- **
- ** If there are no free slots,derror is called
- */
-
- getrange(varmap)
- int *varmap;
- {
- register int i, map, bit;
-
- map = *varmap;
-
- for (i = 0; i < MAXRANGE; i++)
- {
- /* if slot is used, continue */
- if ((bit = 1 << i) & map)
- continue;
-
- map |= bit; /* "or" bit into the map */
- *varmap = map;
-
- # ifdef xDTR1
- if (tTf(40, 10))
- printf("Assn var %d, map %o\n", i, map);
- # endif
-
- return (i);
- }
- derror(NODESCAG);
- return (-1);
- }
-
-
- checkagg(aghead1, aop1, aghead2, aop2)
- QTREE *aghead1;
- QTREE *aop1;
- QTREE *aghead2;
- QTREE *aop2;
- {
- register QTREE *aop_1, *aop_2, *agg1;
- int ok;
-
- /* two aggregate functions can be run together
- ** according to the following table:
- **
- ** prime !prime don't care
- **
- ** prime afcn? never afcn?
- ** !prime never always always
- ** don't care afcn? always always
- **
- ** don't care includes: ANY, MIN, MAX
- ** afcn? means only if a-fcn's are identical
- */
-
- aop_1 = aop1;
- aop_2 = aop2;
- agg1 = aghead1;
-
- if (!prime(aop_1) && !prime(aop_2))
- ok = TRUE;
- else
- if (sameafcn(aop_1->right, aop_2->right))
- ok = cprime(aop_1) && cprime(aop_2);
- else
- ok = FALSE;
- /* The two aggregates must range over the same variables */
- if ((agg1->sym.value.sym_root.lvarm | agg1->sym.value.sym_root.rvarm) != (aghead2->sym.value.sym_root.lvarm | aghead2->sym.value.sym_root.rvarm))
- ok = FALSE;
-
-
- /* check the qualifications */
- if (ok)
- ok = sameafcn(agg1->right, aghead2->right);
- return (ok);
- }
-
-
- sameagg(aghead, newaop, agg_depth)
- QTREE *aghead;
- QTREE *newaop;
- int agg_depth;
- {
- register QTREE *agg, *newa;
- register int i;
-
- agg = aghead;
- newa = newaop;
- agg = agg->left;
- if (agg->sym.type == BYHEAD)
- agg = agg->right;
-
- /* agg now points to first aggregate */
- for (i = 1; agg; agg = agg->left, i++)
- if (sameafcn(agg->right, newa->right) && agg->sym.value.sym_resdom.resno == newa->sym.value.sym_op.opno)
- {
- # ifdef xDTR1
- if (tTf(40, 6))
- printf("found identical aop %x\n", newa);
- # endif
- return (agg_depth - i);
- }
-
- /* no match */
- return (-1);
- }
-
-
-
-
- opt_bylist(alist, root)
- struct agglist *alist;
- QTREE *root;
- {
- register struct agglist *al;
- register QTREE *agg;
- register struct hitlist *hl;
- QTREE **tpr, *tree, *lnodv[MAXDOM+2];
- struct hitlist hlist[30];
- int anyop, i, usedmap, vars, treemap;
-
- /* compute bitmap of all possible vars in tree (can include xtra vars) */
- treemap = root->sym.value.sym_root.lvarm | root->sym.value.sym_root.rvarm;
- anyop = FALSE;
-
- /* scan the list of aggregates looking for one nested in root */
- for (al = alist; (agg = al->agpoint) && agg != root; al++)
- {
- if (agg->sym.type == AGHEAD && agg->left->sym.type == BYHEAD &&
- al->agfather == root)
- {
-
- /* this aggregate function is nested in root */
-
- /* make sure it has some vars of interest */
- if ((treemap & varfind(agg->left->left, (QTREE *)NULL)) == 0)
- continue;
-
- # ifdef xDTR1
- if (tTf(40, 11))
- {
- printf("nested agg\n");
- treepr(agg);
- }
- # endif
-
- /* form list of bydomains */
- lnodv[lnode(agg->left->left, lnodv, 0)] = 0;
- usedmap = 0;
-
- De.de_hnext = &hlist[0];
- De.de_hlimit = &hlist[30];
-
- /* find all possible replacements */
- vars = modtree(&root, lnodv, &usedmap);
-
- /*
- ** All references to a variable must be replaced
- ** in order to use this aggregates by-domains.
- */
- if (usedmap && ((vars & usedmap) == 0))
- {
- # ifdef xDTR1
- if (tTf(40, 7))
- printf("Committed\n");
- # endif
- /* success. Committ the tree changes */
- De.de_hnext->trepr = NULL;
-
- for (hl = &hlist[0]; tpr = hl->trepr; hl++)
- {
- /* get bydomain number */
- i = hl->byno;
-
- /* get node being replaced */
- tree = *tpr;
-
- /* if it is already a var, just change it */
- if (tree->sym.type == VAR)
- {
- tree->sym.value.sym_var.varno = al->agvarno;
- tree->sym.value.sym_var.attno = i + 2;
- }
- else
- *tpr = makavar(lnodv[i], al->agvarno, i + 2);
-
- anyop = TRUE;
- # ifdef xDTR1
- if (tTf(40, 7))
- {
- printf("modified tree\n");
- treepr(*tpr);
- }
- # endif
- }
- }
- /* vars is now a map of the variables in the root */
- treemap = vars;
- }
- }
-
- /* if any changes were made, get rid of the unnecessary links */
- if (anyop)
- chklink(root);
- }
-
-
-
-
- modtree(pnode, lnodv, replmap)
- QTREE **pnode;
- QTREE *lnodv[];
- int *replmap;
- {
- register QTREE *tree;
- register int vars, i;
- QTREE *afcn;
-
- /* point up current node */
- if ((tree = *pnode) == NULL)
- return (0);
-
- /* examine each by-list for match on this subtree */
- for (i = 0; afcn = lnodv[i]; i++)
- {
- if (sameafcn(tree, afcn->right))
- {
- # ifdef xDTR1
- if (tTf(40, 9))
- {
- printf("potential Jackpot");
- treepr(tree);
- }
- # endif
- vars = varfind(tree, (QTREE *)NULL);
- if (De.de_hnext == De.de_hlimit)
- return (vars); /* no more room */
-
- /* record what needs to be done */
- De.de_hnext->trepr = pnode;
- De.de_hnext->byno = i;
- De.de_hnext++;
- *replmap |= vars;
- return (0);
- }
- }
- if (tree->sym.type == VAR)
- return (01 << tree->sym.value.sym_var.varno);
-
- /* try the subtrees */
- vars = modtree(&(tree->left), lnodv, replmap);
- if ((vars & *replmap) == 0)
- vars |= modtree(&(tree->right), lnodv, replmap);
-
- return (vars);
- }
-
-
- chklink(root)
- QTREE *root;
- {
- register QTREE *r, *b, *last;
-
- last = root;
-
- for (r = last->right; r->sym.type != QLEND; r = r->right)
- {
- /* if this is an EQ node then check for an unnecessary compare */
- if ((b = r->left)->sym.type == BOP && b->sym.value.sym_op.opno == opEQ)
- {
- if (sameafcn(b->left, b->right))
- {
- # ifdef xDTR1
- if (tTf(40, 5))
- {
- printf("unnec clause\n");
- treepr(b);
- }
- # endif
- last->right = r->right;
- continue;
- }
- }
- last = r;
- }
- }
-
-
-
- sameafcn(q1, q2)
- QTREE *q1, *q2;
- {
-
- register QTREE *t1, *t2;
- register int len;
- int type;
-
- t1 = q1;
- t2 = q2;
-
- if (!(t1 && t2))
- return(!(t1 || t2));
- len = (t1->sym.len & I1MASK) + SYM_HDR_SIZ;
- type = t1->sym.type;
- if (type == VAR)
- len = sizeof(struct varnode);
- if (type == AND)
- len = 2;
- if (!bequal(&t1->sym.type, &t2->sym.type, len))
- return(0);
- return(sameafcn(t1->left,t2->left) && sameafcn(t1->right,t2->right));
- }
- /*
- ** varfind -- find all variables in the tree pointed to by "root".
- ** Examine all parts of the tree except aggregates. For
- ** aggregates, ignore simple aggregate and look only
- ** at the by-lists of aggregate functions. If the aggregate
- ** is "aghead" then ignore it. There is no reason to look
- ** at yourself!!!!
- ** This routine is called by byeval() to determine
- ** whether to link the aggregate to the root tree.
- **
- ** Curiosity note: since the tree being examined has not been
- ** processed by decomp yet, ckvar does not need to be called
- ** since the var could not have been altered.
- */
-
- varfind(root, aghead)
- QTREE *root;
- QTREE *aghead;
- {
- register QTREE *tree;
- register int type;
-
- if ((tree = root) == NULL)
- return (0);
-
- if ((type = tree->sym.type) == AGHEAD)
- {
- /* ignore if it matches aghead */
- if (tree == aghead)
- return (0);
- /* if this is an aggregate function, look at bylist */
- tree = tree->left;
- if ((type = tree->sym.type) != BYHEAD)
- return (0);
- }
-
- if (type == VAR)
- return (1 << tree->sym.value.sym_var.varno);
-
- return (varfind(tree->left, aghead) | varfind(tree->right, aghead));
- }
-